import React, { FC } from 'react';
import { Path, Range, Text, Node } from 'slate';
import Prism from 'prismjs';
import { css } from 'emotion';

import { CodeText, RenderLeafPropsAttributes } from '../slate';

const getLength = (token: string | Prism.Token): number => {
  if (typeof token === 'string') {
    return token.length;
  } else if (typeof token.content === 'string') {
    return token.content.length;
  } else {
    if (token.content instanceof Array) {
      return token.content.reduce((l, t) => l + getLength(t), 0);
    }
    return getLength(token.content);
  }
};

export const codeDecorate = ([node, path]: [Node, Path]): Range[] => {
  const ranges: Range[] = [];

  if (Text.isText(node) && node.code) {
    const tokens = Prism.tokenize(node.text, Prism.languages['js']);
    let start = 0;

    for (const token of tokens) {
      const length = getLength(token);
      const end = start + length;

      if (typeof token !== 'string') {
        ranges.push({
          [token.type]: true,
          anchor: { path, offset: start },
          focus: { path, offset: end },
        });
      }

      start = end;
    }

    return ranges;
  }
  return ranges;
};

interface CodeProps {
  attributes: RenderLeafPropsAttributes;
  leaf: CodeText;
}

export const Code: FC<CodeProps> = (props) => {
  const { attributes, children, leaf } = props;
  return (
    <span
      {...attributes}
      className={css`
        font-family: monospace;
        background: hsla(0, 0%, 100%, 0.5);

        ${leaf.comment &&
        css`
          color: slategray;
        `}

        ${(leaf.operator || leaf.url) &&
        css`
          color: #9a6e3a;
        `}
        ${leaf.keyword &&
        css`
          color: #07a;
        `}
        ${(leaf.variable || leaf.regex) &&
        css`
          color: #e90;
        `}
        ${(leaf.number || leaf.boolean || leaf.tag || leaf.constant || leaf.symbol || leaf['attr-name'] || leaf.selector) &&
        css`
          color: #905;
        `}
        ${leaf.punctuation &&
        css`
          color: #999;
        `}
        ${(leaf.string || leaf.char) &&
        css`
          color: #690;
        `}
        ${(leaf.function || leaf['class-name']) &&
        css`
          color: #dd4a68;
        `}
      `}
    >
      {children}
    </span>
  );
};
